/*
 * fat32.h
 *
 *  Created on: 25/10/2011
 *      Author: utn_so
 */

#ifndef FAT32_H_
#define FAT32_H_

/***************************************************************************/
/****************************** INCLUDES ***********************************/
/***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <pthread.h>

#include "utils.h"

/***************************************************************************/
/****************************** DEFINES ************************************/
/***************************************************************************/

#define ARCHIVO_DISCO		"/home/utn_so/Desarrollo/Workspace/PFS/prueba.disk"


#define LARGO_SECTOR			512				/*	hay que usarlo lo menos posible,
 	 	 	 	 	 	 	 	 	 	 	 	 	siempre hay que manejarse con la struct bootSector	*/

#define SIGNAL_NO_MORE			-1

#define BAD_CLUSTER				0x0FFFFFF7

#define ClnShutBitMask 			0x08000000		/*	If bit is 1, volume is “clean”.
													If bit is 0, volume is “dirty”.	*/

#define HrdErrBitMask   		0x04000000		/*	If this bit is 1, no disk read/write
													errors were encountered.
													If this bit is 0, the file system driver
													encountered a disk I/O  error on the Volume
													the last time it was mounted					*/

#define FSI_LEAD_SIG			0x41615252
#define FSI_STRUCS_SIG			0x61417272
#define FSI_TRAIL_SIG			0xAA550000

/*	File attributes	*/
#define	ATTR_READ_ONLY			0x01
#define	ATTR_HIDDEN				0x02
#define	ATTR_SYSTEM				0x04
#define	ATTR_VOLUME_ID			0x08
#define	ATTR_DIRECTORY			0x10
#define	ATTR_ARCHIVE			0x20
#define ATTR_LONG_NAME			ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID
#define ATTR_LONG_NAME_MASK		ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_DIRECTORY | ATTR_ARCHIVE

/*	DIR_Name[0]	*/
#define DIR_Name_FREE			0xE5
#define DIR_Name_POWER_FREE		0x00				/*	all of the DIR_Name[0] bytes in all of the entries
														after this one are also set to 0).
														The special 0 value, rather than the 0xE5 value,
														indicates to FAT file system driver code that the
														rest of the entries in this directory do not need
														to be examined because they are all free.			*/

#define ESPACIO_SHORT_NAME				0x20
#define	SHORT_NAME_WITH_LFN_SIGNAL_1	0x7E
#define	SHORT_NAME_WITH_LFN_SIGNAL_2	0x31
#define DOT_DotDot						0x2E

/*	moviemientos de . y ..	muuuy exagerado	*/
#define CANTIDAD_MAX_MOVIEMIENTOS		50

#define ARCHIVO							1
#define DIRECTORIO						2

/***************************************************************************/
/******************************** STRUCS  **********************************/
/***************************************************************************/

struct DSKSZTOSECPERCLUS
{
	uint32_t		DiskSize;
	unsigned char	SecPerClusVal;
};

/*
* This is the table for FAT32 drives. NOTE that this table includes
* entries for disk sizes smaller than 512 MB even though typically
* only the entries for disks >= 512 MB in size are used.
* The way this table is accessed is to look for the first entry
* in the table for which the disk size is less than or equal
* to the DiskSize field in that table entry. For this table to
* work properly BPB_RsvdSecCnt must be 32, and BPB_NumFATs
* must be 2. Any of these values being different may require the first
* table entries DiskSize value to be changed otherwise the cluster count
* may be to low for FAT32.
*/
/*struct DSKSZTOSECPERCLUS DskTableFAT32 [] =
	{*/
/*        {       66600,   	 0},	*//* disks up to 32.5 MB, the 0 value for SecPerClusVal trips an error */
/*        {      532480,		 1},*/   	/* disks up to 260 MB,  .5k cluster */
/*        {    16777216, 		 8},*/    /* disks up to     8 GB,    4k cluster */
/*        {    33554432, 		16},*/    /* disks up to   16 GB,    8k cluster */
/*        {    67108864, 		32},*/    /* disks up to   32 GB,  16k cluster */
/*        {  0xFFFFFFFF,	 	64}*/		/* disks greater than 32GB, 32k cluster */
/*    };	*/


struct bootSector
{

	unsigned char BS_jmpBoot[3];
	unsigned char BS_OEMName[9];						/*	string 8 + 1	*/
	uint16_t BPB_BytsPerSec;
	uint32_t BPB_SecPerClus;
	uint16_t BPB_RsvdSecCnt;
	uint8_t  BPB_NumFATs;
	uint16_t BPB_RootEntCnt;
	uint16_t BPB_TotSec16;
	unsigned char BPB_Media;
	uint16_t BPB_FATSz16;
	uint16_t BPB_SecPerTrk;
	uint16_t BPB_NumHeads;
	uint32_t BPB_HiddSec;
	uint32_t BPB_TotSec32;

	/*FAT32 Structure Starting at Offset 36*/

	uint32_t BPB_FATSz32;
	unsigned char BPB_ExtFlags[2];
	unsigned char BPB_FSVer[2];
	uint32_t BPB_RootClus;
	uint16_t BPB_FSInfo;
	uint16_t BPB_BkBootSec;
	unsigned char BPB_Reserved[12];
	unsigned char BS_DrvNum[1];
	unsigned char BS_Reserved1[1];
	unsigned char BS_BootSig[1];
	uint32_t BS_VolID;
	unsigned char BS_VolLab[12];						/*	string 11 + 1	*/
	unsigned char BS_FilSysType[9]; 					/*	string 8 + 1	*/

};


/*	FAT32 FSInfo Sector Structure and Backup Boot Sector	*/
struct FSInfo
{

	uint32_t FSI_LeadSig;
	unsigned char FSI_Reserved1[480];
	uint32_t FSI_StrucSig;
	uint32_t FSI_Free_Count;
	uint32_t FSI_Nxt_Free;
	unsigned char FSI_Reserved2[12];
	uint32_t FSI_TrailSig;

};


/*	FAT 32 Byte Directory Entry Structure	*/
struct DirStruct
{

	unsigned char DIR_Name[11];
	unsigned char DIR_Attr;
	unsigned char DIR_NTRes;
	unsigned char DIR_CrtTimeTenth;
	unsigned char DIR_CrtTime[2];
	unsigned char DIR_CrtDate[2];
	unsigned char DIR_LstAccDate[2];
	unsigned char DIR_FstClusHI[2];
	unsigned char DIR_WrtTime[2];
	unsigned char DIR_WrtDate[2];
	unsigned char DIR_FstClusLO[2];
	unsigned char DIR_FileSizePOSTA[4];
	uint32_t DIR_FileSize;

};

#define LARGO_STRUCT_DIRECTORY 32

/*	FAT Long Directory Entry Structure	*/

struct LongDirStruct
{

	unsigned char LDIR_Ord;
	unsigned char LDIR_Name1[10];
	unsigned char LDIR_Attr;
	unsigned char LDIR_Type;
	unsigned char LDIR_Chksum;
	unsigned char LDIR_Name2[12];
	unsigned char LDIR_FstClusLO[2];
	unsigned char LDIR_Name3[4];

};

/*	Lista de Directorios	*/
typedef struct {

	struct DirStruct* directorio;			/*	Directorio/Archivo	*/
	int32_t* clusters;						/*	Cluster encadenados	*/
	char* nombreLargo;						/*	Nombre Largo, 13 caracteres	*/
	uint32_t posicionEntrada;
	uint32_t parentCluster;
	struct ListaDirectorios* siguiente;

} ListaDirectorios;

/***************************************************************************/
/****************************** FUNCIONES  *********************************/
/***************************************************************************/

/*	FAT32	*/
int32_t leerBootSector(struct bootSector* );
int32_t leerFileSystemInfo( uint16_t, struct FSInfo*, struct bootSector*);
uint32_t interpretarEntradaDirectorioArchivo(struct DirStruct*, unsigned char*);
uint32_t interpretarEntradaLFN(struct LongDirStruct*, unsigned char*);
unsigned char ChkSum (unsigned char*);
int32_t ponerEnClusterDir(unsigned char*, struct DirStruct*, int32_t );
int32_t ponerEnClusterLFN(unsigned char*, struct LongDirStruct*, int32_t );
int32_t verificarPath(struct bootSector* boot, const char* path, ListaDirectorios* directorioFound);
int32_t leerFAT32DirectoryEntries(struct bootSector* bootSector, uint32_t cluster, ListaDirectorios* firstDir,ListaDirectorios* lastDir);
unsigned char *traerCluster(struct bootSector* boot, int32_t sectorIni);
uint32_t firstDataSector(struct bootSector*);
uint32_t firstSectorofCluster(struct bootSector*, uint32_t);
uint32_t totalDataSec(struct bootSector*);
uint32_t tirateUnLS(ListaDirectorios*);
uint32_t cantidadClustersData(struct bootSector*);
uint32_t proximaEntryDir(struct DirStruct*);
uint32_t printfDirectorio(struct DirStruct*);
int32_t traerNombreShort(struct DirStruct* directorio, char* nombre);
int32_t crearEntradaDIR(struct bootSector* boot, char* nombre, struct DirStruct* dir, int32_t modo);
int32_t crearEntradaLFN(char* nombre, struct DirStruct* dir, struct LongDirStruct* lfn);
int32_t verificarNombreDistinto(ListaDirectorios* firstDir, char* nombreArchivo);
uint32_t dameClusterSegunOffset(struct bootSector* boot, int32_t offset);
int32_t crearEntradaCluster(struct bootSector*, uint32_t, struct DirStruct*,struct LongDirStruct*);
int32_t obtenerDosPosicionesLibres(struct bootSector* boot, uint32_t cluster, unsigned char* clusterBuffer, int32_t* posicion);
int32_t modificarDir(struct bootSector* boot, ListaDirectorios* ldir, struct LongDirStruct* lfn);
int32_t crearEntradaDOT_DOTDOT(struct bootSector* boot, uint32_t cluster, uint32_t parentCluster);
int32_t llenarDOT_DOTDOT(struct DirStruct* dot, struct DirStruct* dotdot);
int32_t borrarEntradaCluster(struct bootSector* boot, ListaDirectorios* ldir);
int32_t borrarEntradaClusterDIR(struct bootSector* boot, ListaDirectorios* ldir);
int32_t cantidadClusterSegunTamanio(struct bootSector* boot, uint32_t size);
int32_t cantidadClusterActualDir(struct bootSector* boot, ListaDirectorios* dir);
int32_t modificarDOTDOTDir(struct bootSector* boot, uint32_t cluster, int32_t valorDOTDOT);
int32_t crearCadenaCluster(struct bootSector* boot, ListaDirectorios* dir);
int32_t liberarDirectorio(ListaDirectorios* ldir);
int32_t liberarListaDirectorio(ListaDirectorios* ini);


/*	AUXILIARES	*/
uint64_t htoi(char s[]);
char* hexToChar(int );
char hexToChar2(int numero);
int32_t valorHighChar(char);
int32_t valorLowChar(char);
char* stringToUpper(char*);
char* stringToLower(char*);

#endif /* FAT32_H_ */
